/*-------------------------------------------------------------------------
 *
 * nodeTidrangescan.c
 *	  支持关系的TID范围扫描的例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeTidrangescan.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/relscan.h"
#include "access/sysattr.h"
#include "access/tableam.h"
#include "catalog/pg_operator.h"
#include "executor/execdebug.h"
#include "executor/nodeTidrangescan.h"
#include "nodes/nodeFuncs.h"
#include "storage/bufmgr.h"
#include "utils/rel.h"


#define IsCTIDVar(node)  \
	((node) != NULL && \
	 IsA((node), Var) && \
	 ((Var *) (node))->varattno == SelfItemPointerAttributeNumber && \
	 ((Var *) (node))->varlevelsup == 0)

typedef enum
{
	TIDEXPR_UPPER_BOUND,
	TIDEXPR_LOWER_BOUND
} TidExprType;

/* 扫描的上限或下限 */
typedef struct TidOpExpr
{
	TidExprType exprtype;		/* 操作的类型；上限或下限 */
	ExprState  *exprstate;		/* 产生 TID 的子表达式的 ExprState */
	bool		inclusive;		/* 操作是否包含 */
} TidOpExpr;

/*
 * 对给定的 'expr'，构建并返回适当的 TidOpExpr，考虑到 
 * expr 的运算符和操作数顺序。
 */
static TidOpExpr *
fc_MakeTidOpExpr(OpExpr *fc_expr, TidRangeScanState *fc_tidstate)
{
	Node	   *fc_arg1 = get_leftop((Expr *) fc_expr);
	Node	   *fc_arg2 = get_rightop((Expr *) fc_expr);
	ExprState  *fc_exprstate = NULL;
	bool		fc_invert = false;
	TidOpExpr  *fc_tidopexpr;

	if (IsCTIDVar(fc_arg1))
		fc_exprstate = ExecInitExpr((Expr *) fc_arg2, &fc_tidstate->ss.ps);
	else if (IsCTIDVar(fc_arg2))
	{
		fc_exprstate = ExecInitExpr((Expr *) fc_arg1, &fc_tidstate->ss.ps);
		fc_invert = true;
	}
	else
		elog(ERROR, "could not identify CTID variable");

	fc_tidopexpr = (TidOpExpr *) palloc(sizeof(TidOpExpr));
	fc_tidopexpr->inclusive = false;	/* 目前为止 */

	switch (fc_expr->opno)
	{
		case TIDLessEqOperator:
			fc_tidopexpr->inclusive = true;
			/* fall through */
		case TIDLessOperator:
			fc_tidopexpr->exprtype = fc_invert ? TIDEXPR_LOWER_BOUND : TIDEXPR_UPPER_BOUND;
			break;
		case TIDGreaterEqOperator:
			fc_tidopexpr->inclusive = true;
			/* fall through */
		case TIDGreaterOperator:
			fc_tidopexpr->exprtype = fc_invert ? TIDEXPR_UPPER_BOUND : TIDEXPR_LOWER_BOUND;
			break;
		default:
			elog(ERROR, "could not identify CTID operator");
	}

	fc_tidopexpr->exprstate = fc_exprstate;

	return fc_tidopexpr;
}

/*
 * 提取出产生 TID 的 qual 子表达式以进行搜索，
 * 并在他们是普通表达式时将其编译为 ExprStates。
 */
static void fc_TidExprListCreate(TidRangeScanState *fc_tidrangestate)
{
	TidRangeScan *fc_node = (TidRangeScan *) fc_tidrangestate->ss.ps.plan;
	List	   *fc_tidexprs = NIL;
	ListCell   *fc_l;

	foreach(fc_l, fc_node->tidrangequals)
	{
		OpExpr	   *fc_opexpr = lfirst(fc_l);
		TidOpExpr  *fc_tidopexpr;

		if (!IsA(fc_opexpr, OpExpr))
			elog(ERROR, "could not identify CTID expression");

		fc_tidopexpr = fc_MakeTidOpExpr(fc_opexpr, fc_tidrangestate);
		fc_tidexprs = lappend(fc_tidexprs, fc_tidopexpr);
	}

	fc_tidrangestate->trss_tidexprs = fc_tidexprs;
}

/* ----------------------------------------------------------------
 *		TidRangeEval
 *
 *		通过评估 trss_tidexprs 计算并设置节点的块和偏移范围进行扫描。
 *		如果检测到范围不能包含任何元组，则返回 false。
 *		如果范围可能包含元组，则返回 true。
 * ----------------------------------------------------------------
 */
static bool fc_TidRangeEval(TidRangeScanState *fc_node)
{
	ExprContext *fc_econtext = fc_node->ss.ps.ps_ExprContext;
	ItemPointerData fc_lowerBound;
	ItemPointerData fc_upperBound;
	ListCell   *fc_l;

	/*
	 * 将上限和下限设置为 ItemPointer 类型范围的绝对限制。
	 * 下面我们将尝试通过查看 TidOpExprs 来缩小这个范围。
	 */
	ItemPointerSet(&fc_lowerBound, 0, 0);
	ItemPointerSet(&fc_upperBound, InvalidBlockNumber, PG_UINT16_MAX);

	foreach(fc_l, fc_node->trss_tidexprs)
	{
		TidOpExpr  *fc_tidopexpr = (TidOpExpr *) lfirst(fc_l);
		ItemPointer fc_itemptr;
		bool		fc_isNull;

		/* 评估此上限。 */
		fc_itemptr = (ItemPointer)
			DatumGetPointer(ExecEvalExprSwitchContext(fc_tidopexpr->exprstate,
													  fc_econtext,
													  &fc_isNull));

		/* 如果上限为 NULL，*没有*与 qual 匹配。 */
		if (fc_isNull)
			return false;

		if (fc_tidopexpr->exprtype == TIDEXPR_LOWER_BOUND)
		{
			ItemPointerData fc_lb;

			ItemPointerCopy(fc_itemptr, &fc_lb);

			/*
			 * 将非包含范围标准化为包含。结果 
			 * 此处的 ItemPointer 可能不是有效的项指针。
			 */
			if (!fc_tidopexpr->inclusive)
				ItemPointerInc(&fc_lb);

			/* 检查我们是否可以使用此 qual 来缩小范围 */
			if (ItemPointerCompare(&fc_lb, &fc_lowerBound) > 0)
				ItemPointerCopy(&fc_lb, &fc_lowerBound);
		}

		else if (fc_tidopexpr->exprtype == TIDEXPR_UPPER_BOUND)
		{
			ItemPointerData fc_ub;

			ItemPointerCopy(fc_itemptr, &fc_ub);

			/*
			 * 将非包含范围标准化为包含。结果 
			 * 此处的 ItemPointer 可能不是有效的项指针。
			 */
			if (!fc_tidopexpr->inclusive)
				ItemPointerDec(&fc_ub);

			/* 检查我们是否可以使用此 qual 来缩小范围 */
			if (ItemPointerCompare(&fc_ub, &fc_upperBound) < 0)
				ItemPointerCopy(&fc_ub, &fc_upperBound);
		}
	}

	ItemPointerCopy(&fc_lowerBound, &fc_node->trss_mintid);
	ItemPointerCopy(&fc_upperBound, &fc_node->trss_maxtid);

	return true;
}

/* ----------------------------------------------------------------
 *		TidRangeNext
 *
 *		使用 TidRangeScanState 信息中的 TIDs 从 TidRangeScan 节点的 currentRelation 
 *		检索元组。
 *
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_TidRangeNext(TidRangeScanState *fc_node)
{
	TableScanDesc fc_scandesc;
	EState	   *fc_estate;
	ScanDirection fc_direction;
	TupleTableSlot *fc_slot;

	/*
	 * 从 TID 扫描节点提取必要的信息 
	 */
	fc_scandesc = fc_node->ss.ss_currentScanDesc;
	fc_estate = fc_node->ss.ps.state;
	fc_slot = fc_node->ss.ss_ScanTupleSlot;
	fc_direction = fc_estate->es_direction;

	if (!fc_node->trss_inScan)
	{
		/* 第一次遍历，计算要扫描的 TID 范围 */
		if (!fc_TidRangeEval(fc_node))
			return NULL;

		if (fc_scandesc == NULL)
		{
			fc_scandesc = table_beginscan_tidrange(fc_node->ss.ss_currentRelation,
												fc_estate->es_snapshot,
												&fc_node->trss_mintid,
												&fc_node->trss_maxtid);
			fc_node->ss.ss_currentScanDesc = fc_scandesc;
		}
		else
		{
			/* 使用更新的 TID 范围重新扫描 */
			table_rescan_tidrange(fc_scandesc, &fc_node->trss_mintid,
								  &fc_node->trss_maxtid);
		}

		fc_node->trss_inScan = true;
	}

	/* 获取下一个元组。 */
	if (!table_scan_getnextslot_tidrange(fc_scandesc, fc_direction, fc_slot))
	{
		fc_node->trss_inScan = false;
		ExecClearTuple(fc_slot);
	}

	return fc_slot;
}

/*
 * TidRangeRecheck -- 访问方法例程以在 EvalPlanQual 中重新检查元组
 */
static bool fc_TidRangeRecheck(TidRangeScanState *fc_node, TupleTableSlot *fc_slot)
{
	return true;
}

/* ----------------------------------------------------------------
 *		ExecTidRangeScan(node)
 *
 *		使用 tids 扫描关系并返回下一个符合条件的元组。
 *		我们调用 ExecScan() 函数并传递适当的 
 *		访问方法函数。
 *
 *		条件：
 *		  -- AMI 维护的“游标”位于之前返回的元组上。
 *
 *		初始状态：
 *		  -- 指示的关系已打开以进行 TID 范围扫描。
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_ExecTidRangeScan(PlanState *fc_pstate)
{
	TidRangeScanState *fc_node = castNode(TidRangeScanState, fc_pstate);

	return ExecScan(&fc_node->ss,
					(ExecScanAccessMtd) fc_TidRangeNext,
					(ExecScanRecheckMtd) fc_TidRangeRecheck);
}

/* ----------------------------------------------------------------
 *		ExecReScanTidRangeScan(node)
 * ----------------------------------------------------------------
 */
void ExecReScanTidRangeScan(TidRangeScanState *fc_node)
{
	/* 将扫描标记为未进行中，且 tid 范围列表尚未计算 */
	fc_node->trss_inScan = false;

	/*
	 * 我们必须等到 TidRangeNext 之前才能调用 table_rescan_tidrange。
	 */
	ExecScanReScan(&fc_node->ss);
}

/* ----------------------------------------------------------------
 *		ExecEndTidRangeScan
 *
 *		释放通过 C 例程分配的任何存储。
 *		不返回任何内容。
 * ----------------------------------------------------------------
 */
void ExecEndTidRangeScan(TidRangeScanState *fc_node)
{
	TableScanDesc fc_scan = fc_node->ss.ss_currentScanDesc;

	if (fc_scan != NULL)
		table_endscan(fc_scan);

	/*
	 * 释放表达式上下文
	 */
	ExecFreeExprContext(&fc_node->ss.ps);

	/*
	 * 清空元组表插槽
	 */
	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);
	ExecClearTuple(fc_node->ss.ss_ScanTupleSlot);
}

/* ----------------------------------------------------------------
 *		ExecInitTidRangeScan
 *
 *		初始化 tid 范围扫描的状态信息，创建 
 *		扫描键，并打开扫描关系。
 *
 *		参数：
 *		  node: 由规划器生成的 TidRangeScan 节点。
 *		  estate: 在 InitPlan 中初始化的执行状态。
 * ----------------------------------------------------------------
 */
TidRangeScanState *
ExecInitTidRangeScan(TidRangeScan *fc_node, EState *fc_estate, int fc_eflags)
{
	TidRangeScanState *fc_tidrangestate;
	Relation	fc_currentRelation;

	/*
	 * 创建状态结构
	 */
	fc_tidrangestate = makeNode(TidRangeScanState);
	fc_tidrangestate->ss.ps.plan = (Plan *) fc_node;
	fc_tidrangestate->ss.ps.state = fc_estate;
	fc_tidrangestate->ss.ps.ExecProcNode = fc_ExecTidRangeScan;

	/*
	 * 杂项初始化
	 *
	 * 为节点创建表达式上下文
	 */
	ExecAssignExprContext(fc_estate, &fc_tidrangestate->ss.ps);

	/*
	 * 将扫描标记为未在进行中，并且 TID 范围标记为尚未计算
	 */
	fc_tidrangestate->trss_inScan = false;

	/*
	 * 打开扫描关系
	 */
	fc_currentRelation = ExecOpenScanRelation(fc_estate, fc_node->scan.scanrelid, fc_eflags);

	fc_tidrangestate->ss.ss_currentRelation = fc_currentRelation;
	fc_tidrangestate->ss.ss_currentScanDesc = NULL;	/* 这里没有表扫描 */

	/*
	 * 从关系描述符获取扫描类型。
	 */
	ExecInitScanTupleSlot(fc_estate, &fc_tidrangestate->ss,
						  RelationGetDescr(fc_currentRelation),
						  table_slot_callbacks(fc_currentRelation));

	/*
	 * 初始化结果类型和投影。
	 */
	ExecInitResultTypeTL(&fc_tidrangestate->ss.ps);
	ExecAssignScanProjectionInfo(&fc_tidrangestate->ss);

	/*
	 * 初始化子表达式
	 */
	fc_tidrangestate->ss.ps.qual =
		ExecInitQual(fc_node->scan.plan.qual, (PlanState *) fc_tidrangestate);

	fc_TidExprListCreate(fc_tidrangestate);

	/*
	 * 完成所有操作。
	 */
	return fc_tidrangestate;
}
